"""
用户工厂类 - 使用抽象工厂模式
遵循单一职责原则，将创建逻辑从模型中分离
"""
from abc import ABC, abstractmethod
from models.m import User, OAuthAccount
from typing import Tuple, Optional


class UserFactory(ABC):
    """用户工厂抽象类 - 定义用户创建的统一接口"""
    
    @abstractmethod
    async def create_user(self, **kwargs) -> User:
        """
        创建用户的抽象方法（子类必须实现）
        
        Args:
            **kwargs: 创建用户所需的参数
        
        Returns:
            User: 创建的用户对象
        """
        pass


class LocalUserFactory(UserFactory):
    """本地用户工厂 - 负责创建用户名/密码登录的用户"""
    
    async def create_user(self, username: str, password: str) -> User:
        """
        创建本地注册用户（用户名/密码登录）
        
        Args:
            username: 用户名
            password: 密码（明文，由调用方决定是否加密）
        
        Returns:
            User: 创建的用户对象
        
        Raises:
            ValueError: 用户名已存在
        """
        # 检查用户名是否已存在
        if await User.exists(username=username):
            raise ValueError(f"用户名 '{username}' 已被注册")
        
        # 创建用户
        user = User(
            username=username,
            password=password
        )
        await user.save()
        
        return user


class OAuthUserFactory(UserFactory):
    """第三方登录用户工厂 - 负责创建或获取第三方登录用户"""
    
    async def create_user(self, **kwargs) -> Tuple[User, OAuthAccount, bool]:
        """
        创建第三方登录用户（重写父类方法）
        
        注意：返回值与父类不同，返回三元组
        """
        return await self.get_or_create_oauth_user(**kwargs)
    
    async def get_or_create_oauth_user(
        self,
        provider: str,
        provider_user_id: str,
        username: str,
        email: Optional[str] = None,
        phone: Optional[str] = None,
        avatar: Optional[str] = None,
        extra_data: Optional[dict] = None
    ) -> Tuple[User, OAuthAccount, bool]:
        """
        通过第三方登录创建或获取用户
        
        工作流程：
        1. 查找是否已存在该第三方账号绑定
        2. 如果存在，返回关联的用户
        3. 如果不存在：
           - 自动处理用户名冲突（添加后缀）
           - 创建新用户
           - 创建第三方账号绑定
        
        Args:
            provider: 第三方平台名称（dingtalk, wechat, github等）
            provider_user_id: 第三方平台的用户ID
            username: 用户名
            email: 邮箱（可选）
            phone: 手机号（可选）
            avatar: 头像URL（可选）
            extra_data: 额外数据（可选，存储为JSON）
        
        Returns:
            Tuple[User, OAuthAccount, bool]: (用户对象, 第三方账号对象, 是否新创建)
        
        Example:
            >>> user, oauth, is_new = await UserFactory.get_or_create_oauth_user(
            ...     provider="dingtalk",
            ...     provider_user_id="xxx123",
            ...     username="张三",
            ...     phone="13800138000"
            ... )
            >>> print(f"用户: {user.username}, 是否新创建: {is_new}")
        """
        # 1. 查找是否已经绑定过该第三方账号
        oauth_account = await OAuthAccount.get_or_none(
            provider=provider,
            provider_user_id=provider_user_id
        ).prefetch_related('user')
        
        if oauth_account:
            # 已存在绑定，返回关联的用户
            return oauth_account.user, oauth_account, False
        
        # 2. 不存在，需要创建新用户和绑定
        # 处理用户名冲突（自动添加后缀）
        final_username = await self._generate_unique_username(username)
        
        # 3. 创建用户
        user = User(
            username=final_username,
            email=email,
            phone=phone,
            avatar=avatar,
            password=None  # 第三方登录用户没有密码
        )
        await user.save()
        
        # 4. 创建第三方账号绑定
        oauth_account = OAuthAccount(
            user=user,
            provider=provider,
            provider_user_id=provider_user_id,
            extra_data=extra_data or {}
        )
        await oauth_account.save()
        
        return user, oauth_account, True
    
    async def _generate_unique_username(self, base_username: str) -> str:
        """
        生成唯一的用户名（内部方法）
        如果用户名已存在，自动添加数字后缀
        
        Args:
            base_username: 基础用户名
        
        Returns:
            str: 唯一的用户名
        
        Example:
            如果 "张三" 已存在，会依次尝试：
            - 张三_1
            - 张三_2
            - ...
        """
        username = base_username
        counter = 1
        
        # 循环检查，直到找到不重复的用户名
        while await User.exists(username=username):
            username = f"{base_username}_{counter}"
            counter += 1
        
        return username
    
    async def bind_oauth_account(
        self,
        user: User,
        provider: str,
        provider_user_id: str,
        extra_data: Optional[dict] = None
    ) -> OAuthAccount:
        """
        为已存在的用户绑定第三方账号
        
        使用场景：用户已经注册，后续想绑定第三方账号
        
        Args:
            user: 已存在的用户对象
            provider: 第三方平台名称
            provider_user_id: 第三方平台的用户ID
            extra_data: 额外数据
        
        Returns:
            OAuthAccount: 创建的第三方账号绑定对象
        
        Raises:
            ValueError: 该第三方账号已被其他用户绑定
        """
        # 检查该第三方账号是否已被绑定
        existing = await OAuthAccount.get_or_none(
            provider=provider,
            provider_user_id=provider_user_id
        )
        
        if existing:
            raise ValueError(f"该{provider}账号已被其他用户绑定")
        
        # 创建绑定
        oauth_account = OAuthAccount(
            user=user,
            provider=provider,
            provider_user_id=provider_user_id,
            extra_data=extra_data or {}
        )
        await oauth_account.save()
        
        return oauth_account

